/*
    nspire-gamekit - a simple homebrew game framework
    Copyright (C) 2011  Daniel Tang

    This program is gameAutoFree software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the gameAutoFree Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
#include <os.h>
#include "autorelease.h"
#include "timer.h"

/* Oh noes, another doubley linked list :( */

struct game_timer_t {
    game_timer_t *next;
    game_timer_t *prev;
    void* userdata;
    void (*timerfunc)(void*);
    unsigned int interval;
    unsigned int starttime;
    char runonce;
    
};

static game_timer_t* privateTimerAlloc(unsigned int interval, void (*timerfunc)(void*), void* userdata, char runonce);

static game_timer_t* firstElement = NULL;
static game_timer_t* lastElement = NULL;



game_timer_t* gameTimerAlloc(unsigned int interval, void (*timerfunc)(void*), void* userdata) {
    return privateTimerAlloc(interval,timerfunc,userdata,0);
}

void gameTimerRunAfter(unsigned int interval, void (*timerfunc)(void*), void* userdata) {
    privateTimerAlloc(interval,timerfunc,userdata,1);
}

void gameTimerFree(game_timer_t* node) {
    /* Unlink from the double linked list */
    if (node->prev) node->prev->next = node->next;
    if (node->next) node->next->prev = node->prev;
    
    if (firstElement == node) firstElement = node->next;
    if (lastElement == node) lastElement = node->prev;
    
    gameAutoFree(node);
}

void gameTimerTick() {
    static unsigned int lastTimerVal = 0;
    game_timer_t *node = firstElement;
    game_timer_t *next = node;
    unsigned int currentTime = *(unsigned int*)0x90090000;
    unsigned int diffTime;
    
    if (currentTime == lastTimerVal) return;
    
    while (node) { 
        next = node->next;
        diffTime = (currentTime - node->starttime);
        if (diffTime > 0) {
            if ((diffTime % (node->interval)) == 0) {
                if (node->timerfunc) node->timerfunc(node->userdata);
                if (node->runonce) {
                    gameTimerFree(node); 
                }else{
                    node->starttime = currentTime;
                }
            }
        }
        node = next;
    }
    lastTimerVal = currentTime;
}

static inline game_timer_t* privateTimerAlloc(unsigned int interval, void (*timerfunc)(void*), void* userdata, char runonce) {
    if (!interval) return NULL;

    game_timer_t *node = gameAutoMalloc(sizeof(game_timer_t));
    if (!node) return NULL;
    node->next = NULL;
    node->prev = NULL;
    
    node->interval = interval;
    node->userdata = userdata;
    node->timerfunc = timerfunc;
    node->starttime = *(unsigned int*)0x90090000; //RTC
    node->runonce = runonce;
    
    if (firstElement == NULL) {
        firstElement = node;
    }
    if (lastElement) {
        lastElement->next = node;
        node->prev = lastElement;
    }
    lastElement = node;

    return node;
}